// 

//
//	File: Script.cpp
//
// Implements the class Script.

#include "Script.h"
#include "Util.h"
#include <assert.h>



//     _____           _   _             
//    / ____|         | | (_)            
//   | (___   ___  ___| |_ _  ___  _ __  
//    \___ \ / _ \/ __| __| |/ _ \| '_ \ 
//    ____) |  __/ (__| |_| | (_) | | | |
//   |_____/ \___|\___|\__|_|\___/|_| |_|
//                                       




Script::Section::Section() : 
	sections_(INIT_MATERIALSECTION_STACK_NUM), 
	names_(INIT_MATERIALSECTION_STACK_NUM),
	LineID_(INIT_MATERIALVALUE_STACK_NUM),
	LineValue_(INIT_MATERIALVALUE_STACK_NUM)
{
}



Script::Section::Section( char *MaterialStr ) : 
	sections_(INIT_MATERIALSECTION_STACK_NUM), 
	names_(INIT_MATERIALSECTION_STACK_NUM),
	LineID_(INIT_MATERIALVALUE_STACK_NUM),
	LineValue_(INIT_MATERIALVALUE_STACK_NUM)
{
	BuildInfo buildInfo( MaterialStr, true );
	LoadFromMaterialStr( buildInfo );
}


void Script::Section::LoadFromMaterialStr( BuildInfo & buildInfo )
{
	char *line = NULL;
	char *id = NULL;

	while( GetLine( buildInfo, &line ) )
	{
		unsigned int index = 0;

		// get the id
		bool stop = false;
		while( !stop && index < strlen(line) )
		{
			switch( line[index] )
			{
			case '=' :
			case ' ' :
			case '\t':
			case '\0':
				stop = true;
				break;

			case '\n' :
				stop = true;
				break;

			default :
				if( !IsAlphaNumeric( line[index] ) )
				{
					assert(0);
					OyLog_Write_L1(OyXMLLog::Error, OyXMLLog::Engine, L"Script-invalid id name");
				}
				++index;
			}
		}


		// check if it's a section
		bool isSection = false;
		if( index == strlen(line) )
		{
			isSection = true;
		}
		else
		{
			if( Char_Find_first_not_of(line, "\t ", index) == -1 )
				isSection = true;
		}

		// This line is comment
		if( line[0] == '/' && line[1] == '/' )
			continue;
		// This line is space 
		if( strlen(line) == 0 )
			continue;

		if( isSection )
		{
			id = line;
			CharToUpper(id);
		}

		// get the section or value
		if( isSection )
			ReadSection( buildInfo, id );
		else
			ReadValue( buildInfo, line, index );
	}
}



void Script::Section::ReadSection( BuildInfo & buildInfo, char* id )
{
	char *line = NULL;
	GetLine( buildInfo, &line );
	if( strcmp( line, "{" ) != 0 )
	{
		assert(0);
		OyLog_Write_L1(OyXMLLog::Error, OyXMLLog::Engine, L"Script-expected: '{'");
	}

	// create the sub section
	BuildInfo subInfo( buildInfo.Material_Str, false );
	Section * newSection = new Section;
	try
	{
		newSection->LoadFromMaterialStr( subInfo );
	}
	catch ( ... )
	{
		delete newSection;
		newSection = 0;
		throw;
	}

	// Update String Pos
	buildInfo.Material_Str = subInfo.Material_Str;

	if( !sections_.Push( newSection ) )
	{
		sections_.Resize( sections_.Size() + INIT_MATERIALSECTION_STACK_NUM );
		sections_.Push( newSection );
	}
	if( !names_.Push(id) )
	{
		names_.Resize( names_.Size() + INIT_MATERIALSECTION_STACK_NUM );
		names_.Push( id );
	}
}


// ReadValue()
void Script::Section::ReadValue( BuildInfo & buildInfo, char* line, int index )
{
	char *id = line;
	
	// get the value
	index = Char_Find_first_of( line, '=' );
	if( index == -1 )
	{
		assert(0);
		OyLog_Write_L1(OyXMLLog::Error, OyXMLLog::Engine, L"Script-expected: '='");
		return;
	}

	line[index] = 0;
	char *value = line + index + 1;

	int first = Char_Find_first_not_of(value, "= \t");
	int last = Char_Find_last_not_of(value, " \t", strlen(value));
	*(value+last+1) = 0;
	value = value + first;
	LineValue_.Push(value);


	last = Char_Find_last_not_of(id, " \t", strlen(id));
	*(id+last+1) = 0;
	CharToUpper(id);
	LineID_.Push(id);
}


// IsAlphaNumeric()
bool Script::Section::IsAlphaNumeric( char c )
{
	return ( c >= 'a' && c <= 'z' ) ||
		   ( c >= 'A' && c <= 'Z' ) ||
		   ( c >= '0' && c <= '9' ) ||
		   c == '_' || c == '/';
}


// GetLine()
bool Script::Section::GetLine( BuildInfo & buildInfo, char** line )
{
	int StrOffset = 0;
	if( *(buildInfo.Material_Str + StrOffset) == 0 )
		return false;

	*(line) = buildInfo.Material_Str + StrOffset;

	while( *(buildInfo.Material_Str + StrOffset) != '\n' )
	{
		StrOffset++;
	}

	*(buildInfo.Material_Str + StrOffset) = 0;
	StrOffset++;

	buildInfo.Material_Str += StrOffset;
	CharTrimLeftRight(*line);

	if( buildInfo.global && strcmp( *line, "}" )==0 )
	{
		assert(0);
		OyLog_Write_L1(OyXMLLog::Error, OyXMLLog::Engine, L"Script-expected: [id]");
	}
	return (strcmp( *line, "}" )!=0);
}



Script::Section::~Section()
{
	for( int i=0; i<sections_.Count(); i++ )
	{
		if( sections_[i] != NULL )
			delete sections_[i];
		sections_[i] = NULL;
	}
}




int Script::Section::ContainSection( const char* id )
{
	int Found = 0;
	for( int i=0; i<names_.Count(); ++i )
	{
		if( _strnicmp(id, names_[i], strlen(id)) == 0 )
			Found++;
	}
	return Found;
}

Script::Section* Script::Section::GetSection( const char* name, int ID )
{
	int FoundIdx = 0;

	for(int SectionIdx=0; SectionIdx<names_.Count(); SectionIdx++)
	{
		if( _strnicmp(name, names_[SectionIdx], strlen(name) ) == 0 )
		{
			if( FoundIdx == ID )
				return sections_[SectionIdx];
			FoundIdx++;
		}
	}

	return NULL;
}

// ContainString()
bool Script::Section::ContainString( const char* id )
{
	for(int i=0; i<LineID_.Count(); i++)
	{
		const char* aa = LineID_[i];
		if( _strnicmp(id, LineID_[i], strlen(id) ) == 0 )
			return true;
	}

	return false;
}

// GetString()
const char* Script::Section::GetString( const char* id, const char* def )
{
	for(int i=0; i<LineID_.Count(); i++)
	{
		if( _strnicmp(id, LineID_[i], strlen(id) ) == 0 )
			return LineValue_[i];
	}
	return def;
}


// GetInt()
int Script::Section::GetInt( const char* id, int def )
{
	for(int i=0; i<LineID_.Count(); i++)
	{
		if( _strnicmp(id, LineID_[i], strlen(id) ) == 0 )
			return atoi(LineValue_[i]);
	}

	return def;
}


// GetFloat()
float Script::Section::GetFloat( const char* id, float def )
{
	for(int i=0; i<LineID_.Count(); i++)
		if( _strnicmp(id, LineID_[i], strlen(id) ) == 0 )
			return (float)atof(LineValue_[i]);
	return def;
}


// ContainBool()
bool Script::Section::ContainBool( const char* id )
{
	for(int i=0; i<LineID_.Count(); i++)
		if( _strnicmp(id, LineID_[i], strlen(id) ) == 0 )
			return true;
	return false;
}

// GetBool()
bool Script::Section::GetBool( const char* id, bool def )
{
	for(int i=0; i<LineID_.Count(); i++)
	{
		if( _strnicmp(id, LineID_[i], strlen(id) ) == 0 )
		{
			if( _strnicmp( "true", LineValue_[i], 4 ) == 0 )
				return true;
			else if( _strnicmp( "false", LineValue_[i], 5 ) == 0 )
				return false;
		}
	}
	return def;
}

// GetVec4()
OyVector4D Script::Section::GetVec4( const char* id, const OyVector4D & def )
{
	static char NumStr[128];
	OyVector4D Result(0, 0, 0);

	for(int i=0; i<LineID_.Count(); i++)
	{
		if( _strnicmp(id, LineID_[i], strlen(id) ) == 0 )
		{
			float vec[4] = { 0 } ;
			const char *Vec4Str = LineValue_[i];
		
			if( (Vec4Str[0] == '(') && (Vec4Str[strlen(Vec4Str)-1] == ')') )
			{
				strcpy_s( NumStr, Vec4Str+1 );
				NumStr[strlen(Vec4Str)-2] = 0;


				char* pStr = NumStr;
				for(int i = 0; i < 3; ++i )
				{
					int pos = Char_Find_first_of( pStr, ',' );
					if( pos == -1 )
						return def;

					*(pStr+pos) = 0;
					vec[i] = (float)atof(pStr);

					pStr += pos + 1;
				}
				vec[3] = (float)atof(pStr);

				Result.Set(vec[0], vec[1], vec[2], vec[3]);
				return Result;
			}
			return def;
		}
	}
	return def;
}







//     _____           _       _   
//    / ____|         (_)     | |  
//   | (___   ___ _ __ _ _ __ | |_ 
//    \___ \ / __| '__| | '_ \| __|
//    ____) | (__| |  | | |_) | |_ 
//   |_____/ \___|_|  |_| .__/ \__|
//                      | |        
//                      |_|        


// ctor
Script::Script( char* MaterialStr ) :
	global_( NULL )
{
	global_ = new Section( MaterialStr );
}


// dtor
Script::~Script()
{
	delete global_;
	global_ = NULL;
}
